package inv.stask.ui;

import inv.cal.CalendarFactory;
import inv.cal.DatePickerDialog;
import inv.cal.logic.EventsInfo;
import inv.cal.logic.EventsInfo.EventType;
import inv.loader.logic.LoaderException;
import inv.loader.logic.TableExport;
import inv.loader.logic.impl.XLSTableExport;
import inv.loader.logic.impl.XMLTableExport;
import inv.stask.beans.Configuration;
import inv.stask.beans.Task;
import inv.stask.beans.Task.Priority;
import inv.stask.logic.ArchiverUtil;
import inv.stask.logic.ConfigurationUtil;
import inv.stask.logic.Logger;
import inv.stask.logic.TaskUtil;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.swing.DefaultCellEditor;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenuBar;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.RowFilter;
import javax.swing.SwingUtilities;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.filechooser.FileFilter;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
import javax.swing.JTextField;


public class TaskListFrame extends JFrame implements DatePickerAction {

	private static final long serialVersionUID = 1L;
	private JPanel jContentPane = null;
	private JPanel jTablePane = null;
	private JScrollPane jTableScrollPane = null;
	private JMenuBar jJMenuBar = null;
	private JToolBar jToolBar = null;
	private JTable jTable = null;
	private JButton jSaveButton = null;
	private JButton jExportButton = null;
	private JButton jImportButton = null;
	private JToggleButton jHideDoneButton = null;
	private TaskListTableModel tableModel = null;
	private JButton jAddButton = null;
	private DatePickerDialog datePicker = null;
	private Locale locale	= Locale.getDefault();
	
	private JFileChooser impexFileChooser;
	private JErrorComboBox errorBox;
	private JButton jArchiveButton = null;
	
	private final Map<String, TableExport> tableExporters	= new HashMap<String, TableExport>();
	{
		tableExporters.put(".xml", new XMLTableExport());
		tableExporters.put(".xls", new XLSTableExport());
	}

	
	private TableRowSorter<TableModel> sorter = null;
	private JTextField jTagsField = null;
	
	public enum Identifier{DATE, NAME, PRIORITY, DONE};

	/**
	 * This is the default constructor
	 */
	public TaskListFrame() {
		super();
		initialize();
	}
	
	/**
	 * This method initializes jSaveButton	
	 * 	
	 * @return javax.swing.JButton	
	 */
	private JButton getJSaveButton() {
		if (jSaveButton == null) {
			jSaveButton = new JButton();
			jSaveButton.setText("save");
			jSaveButton.addActionListener(new ActionListener(){

				public void actionPerformed(ActionEvent e) {
					new TaskUtil().write(tableModel.getTaskList());
					Logger.addMessage("INFO: Tasks saved ("+tableModel.getTaskList().size()+" items)");
					tableModel.fireTableDataChanged();
				}});
		}
		return jSaveButton;
	}
	
	/**
	 * This method initializes jSaveButton	
	 * 	
	 * @return javax.swing.JButton	
	 */
	private JToggleButton getJHideDoneButton() {
		if (jHideDoneButton== null) {
			jHideDoneButton = new JToggleButton();
			jHideDoneButton.setText("hide done");
			jHideDoneButton.addActionListener(new ActionListener(){
				public void actionPerformed(ActionEvent e) {
					sorter.setRowFilter(null);
					if(jHideDoneButton.isSelected()){
						RowFilter<TableModel, Object> rowFilter	= new RowFilter<TableModel, Object>(){
							@Override
							public boolean include(javax.swing.RowFilter.Entry<? extends TableModel, ? extends Object> entry) {
								Object value = entry.getValue(3);
								if(value instanceof Boolean)
									if(((Boolean)value).equals(Boolean.TRUE))
										return false;
										
								return true;
							}
						};
						sorter.setRowFilter(rowFilter);
					}
				}});
		}
		return jHideDoneButton;
	}

	/**
	 * This method initializes jAddButton	
	 * 	
	 * @return javax.swing.JButton	
	 */
	private JButton getJAddButton() {
		if (jAddButton == null) {
			jAddButton = new JButton();
			jAddButton.setText("add");
			
			jAddButton.addActionListener(new ActionListener(){
				public void actionPerformed(ActionEvent e) {
					tableModel.getTaskList().add(0, Task.newTask());
					tableModel.fireTableRowsInserted(0, 0);
				}
			});
		}
		return jAddButton;
	}

	/**
	 * This method initializes jTagsField	
	 * 	
	 * @return javax.swing.JTextField	
	 */
	private JTextField getJTagsField() {
		if (jTagsField == null) {
			jTagsField = new JTextField();
			jTagsField.addActionListener(new java.awt.event.ActionListener() {
				public void actionPerformed(java.awt.event.ActionEvent e) {
					String value	= jTagsField.getText();
					int x = jTable.getSelectedRow();
					System.out.println("selectedRow="+x);
					System.out.println(value);
					
					System.out.println("actionPerformed()"); // TODO Auto-generated Event stub actionPerformed()
				}
			});
		}
		return jTagsField;
	}

	public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, UnsupportedLookAndFeelException {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				TaskListFrame thisClass = new TaskListFrame();
				thisClass.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
				thisClass.setVisible(true);
			}
		});
	}

	/**
	 * This method initializes this
	 * 
	 * @return void
	 */
	private void initialize() {
		this.setSize(500, 300);
		this.setJMenuBar(getJJMenuBar());
		this.setContentPane(getJContentPane());
		this.setTitle("" + (new SimpleDateFormat("MMM-dd").format(new Date())));
		jTable.setRowHeight(26);
		
		sorter = new TableRowSorter<TableModel>(jTable.getModel());
		sorter.setComparator(jTable.getColumnModel().getColumnIndex(Identifier.DATE), new Comparator<Task>(){
			public int compare(Task o1, Task o2) {
				return o1.getDueDate().compareTo(o2.getDueDate());
			}
		});
		jTable.setRowSorter(sorter);
		if(tableModel.getTaskList().size() > 0){
			sorter.toggleSortOrder(0);
			sorter.sort();
		}
		
		Configuration config	= new ConfigurationUtil().read();
		if(config != null){
			this.setSize(config.getWidth(), config.getHeight());
			this.setLocation(config.getPosX(), config.getPosY());
			Enumeration<TableColumn> en = jTable.getColumnModel().getColumns();

			
			en = jTable.getColumnModel().getColumns();
			if(config.getModelIndex() != null){
				TableColumnModel model	= jTable.getColumnModel();
				int columnCount	= model.getColumnCount();
				for(int i=0;i<columnCount-1;i++){
					TableColumn c1	= model.getColumn(i);
					int index1		= config.getModelIndex().get(i);
					if(c1.getModelIndex() != index1){
						for(int j=0;j<columnCount;j++){
							TableColumn c2	= model.getColumn(j);
							if(c2.getModelIndex() == index1){
								model.moveColumn(j, i);
								break;
							}
						}
					}
				}
			}
			
			int index = 0;
			if(config.getColumnsCurrent() != null)
				while(en.hasMoreElements()){
					TableColumn column	= en.nextElement();
					column.setMinWidth(config.getColumnsMin().get(index));
					column.setMaxWidth(config.getColumnsMax().get(index));
					column.setPreferredWidth(config.getColumnsCurrent().get(index));
					index ++;
				}
				
			// Initialize date picker
			getDatePicker();
			if(config.getDialogPosX() != null)
				datePicker.setLocation(config.getDialogPosX(), config.getDialogPosY());
		}
		
		this.addWindowListener(new WindowListener(){

			public void windowActivated(WindowEvent e) {
			}

			public void windowClosed(WindowEvent e) {
			}

			public void windowClosing(WindowEvent e) {
				Configuration	config	= new Configuration();
				
				if(datePicker != null){
					config.setDialogPosX(datePicker.getLocation().x);
					config.setDialogPosY(datePicker.getLocation().y);
				}
				
				config.setWidth(getWidth());
				config.setHeight(getHeight());
				config.setPosX(getLocation().x);
				config.setPosY(getLocation().y);
				
				Enumeration<TableColumn> en	= jTable.getColumnModel().getColumns();
				int columnCount	= jTable.getColumnCount();
				List<Integer> min	= new ArrayList<Integer>(columnCount); 
				List<Integer> max	= new ArrayList<Integer>(columnCount); 
				List<Integer> current	= new ArrayList<Integer>(columnCount);
				List<Integer> modelIndex = new ArrayList<Integer>(columnCount);
				int index = 0;
				while(en.hasMoreElements()){
					TableColumn column	= en.nextElement();
					min.add(column.getMinWidth());
					max.add(column.getMaxWidth());
					current.add(column.getWidth());
					
					modelIndex.add(column.getModelIndex());
					index ++;
				}
				
				config.setColumnsMin(min);
				config.setColumnsMax(max);
				config.setColumnsCurrent(current);
				config.setModelIndex(modelIndex);
				
				
				
				new ConfigurationUtil().write(config);
			}

			public void windowDeactivated(WindowEvent e) {
			}

			public void windowDeiconified(WindowEvent e) {
			}

			public void windowIconified(WindowEvent e) {
			}

			public void windowOpened(WindowEvent e) {
				Logger.addMessage("INFO: We have begun.");
			}
			
		});
	}
	
	private void initColumnSizes(JTable table) {
		table.getColumnModel().getColumn(0).setMinWidth(90);
		table.getColumnModel().getColumn(0).setMaxWidth(90);
		
		table.getColumnModel().getColumn(1).setMinWidth(260);
		
		table.getColumnModel().getColumn(2).setMinWidth(60);
		table.getColumnModel().getColumn(3).setMinWidth(60);
		table.getColumnModel().getColumn(2).setMaxWidth(60);
		table.getColumnModel().getColumn(3).setMaxWidth(60);
		

		
//		table.getColumnModel().getColumn(0).setResizable(false);
		table.getColumnModel().getColumn(2).setResizable(false);
		table.getColumnModel().getColumn(3).setResizable(false);
		
	}

	/**
	 * This method initializes jContentPane
	 * 
	 * @return javax.swing.JPanel
	 */
	private JPanel getJContentPane() {
		if (jContentPane == null) {
			jContentPane = new JPanel();
			jContentPane.setLayout(new BorderLayout());
			jContentPane.add(getJToolBar(), BorderLayout.NORTH);
			jContentPane.add(getJTablePane(), BorderLayout.CENTER);
			jContentPane.add(getErrorBox(), BorderLayout.SOUTH);
		}
		return jContentPane;
	}
	
	private JPanel getJTablePane() {
		if (jTablePane == null) {
			jTablePane = new JPanel();
			jTablePane.setLayout(new BorderLayout());
			jTablePane.add(getJTable().getTableHeader(), BorderLayout.PAGE_START);
			jTablePane.add(getJTableScrollPane(), BorderLayout.CENTER);
		}
		return jTablePane;
	}
	
	private JScrollPane getJTableScrollPane() {
		if (jTableScrollPane == null) {
			jTableScrollPane = new JScrollPane(getJTable());
		}
		return jTableScrollPane;
	}

	/**
	 * This method initializes jJMenuBar	
	 * 	
	 * @return javax.swing.JMenuBar	
	 */
	private JMenuBar getJJMenuBar() {
		if (jJMenuBar == null) {
			jJMenuBar = new JMenuBar();
		}
		return jJMenuBar;
	}

	/**
	 * This method initializes jToolBar	
	 * 	
	 * @return javax.swing.JToolBar	
	 */
	private JToolBar getJToolBar() {
		if (jToolBar == null) {
			jToolBar = new JToolBar();
			jToolBar.add(getJSaveButton());
			jToolBar.add(getJAddButton());
			jToolBar.add(getJHideDoneButton());
			jToolBar.add(getJImportButton());
			jToolBar.add(getJExportButton());
			jToolBar.add(getJArchiveButton());
			jToolBar.add(getJTagsField());
		}
		return jToolBar;
	}

	/**
	 * This method initializes jTable	
	 * 	
	 * @return javax.swing.JTable	
	 */
	private JTable getJTable() {
		if (jTable == null) {
			tableModel	= new TaskListTableModel();
			tableModel.setTaskList(new TaskUtil().read());
			
			TableColumnModel columnModel	= new TaskListTableColumnModel();
			
			jTable = new JTable(tableModel, columnModel);
			jTable.createDefaultColumnsFromModel();
			for(Identifier id: Identifier.values()){
				columnModel.getColumn(id.ordinal()).setIdentifier(id);
			}
			
			jTable.setDefaultRenderer(Object.class, new TaskRowRenderer());
			setPriorityColumn(jTable, jTable.getColumnModel().getColumn(2));
			setDateColumn(jTable, jTable.getColumnModel().getColumn(0));
			
			initColumnSizes(jTable);

		}
		return jTable;
	}
	
    public void setPriorityColumn(JTable table, TableColumn priorityColumn) {
		//Set up the editor for the sport cells.
		JComboBox comboBox = new JComboBox();
		for(Priority value: Priority.values()){
			comboBox.addItem(value);
		}
		priorityColumn.setCellEditor(new DefaultCellEditor(comboBox));
	}
    
    public void setDateColumn(JTable table, TableColumn dateColumn) {
		dateColumn.setCellEditor(new DateCellEditor(this));
		dateColumn.setCellRenderer(new DateCellRenderer());
	}
    
    protected DatePickerDialog getDatePicker(){
    	if(datePicker == null){
    		datePicker = CalendarFactory.createDatePicker(this, locale, null);
    		datePicker.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
    	}
    	
    	return datePicker;
    }
    
    public Date showDatePicker(Date currentSelection){
    	getDatePicker();
    	
    	List<Task> taskList = tableModel.getTaskList();
    	List<EventsInfo> events	= new ArrayList<EventsInfo>(taskList.size());
    	Date today	= new Date();
    	for(Task task: taskList){
    		Date due = task.getDueDate();
    		String text = task.getName();
    		boolean done	= task.getDone();
    		
    		EventType type = EventType.INFO;
    		if(!done && today.compareTo(due) > 0)
    			type = EventType.WARN;
    		events.add(new EventsInfo(due, text, type));
    	}
    	
    	datePicker.updateEvents(events, currentSelection);
    	datePicker.setVisible(true);
    	return datePicker.getReturnValue();
    }
    
    public void showDatePickerResults(){
    	tableModel.fireTableDataChanged();
    }
    
    private String getExtension(String filename){
    	int lastIndex	= filename.lastIndexOf('.');
	    if(lastIndex >= 0){
	    	String extension	= filename.substring(lastIndex);
	    	return extension.toLowerCase();
	    }
	    return null;
    }
    
    public JFileChooser getFileChooser(){
    	if(impexFileChooser == null){
    		impexFileChooser	= new JFileChooser();
    		impexFileChooser.setFileFilter(new FileFilter(){
    			public final String[] acceptedExtensions = new String[]{".xls", ".xml"};
    			@Override
    			public boolean accept(File f) {
    			    if (f.isDirectory()) {
    			    	return true;
    			    }

    			    String filename = f.getName();
    			    String extension = getExtension(filename);
    			    for(String accepted: acceptedExtensions){
    		    		if(accepted.equalsIgnoreCase(extension))
    		    			return true;
    		    	}
    			    
    			    return false;
    			}

    			@Override
    			public String getDescription() {
    				return "XLS, XML";
    			}
    		});
    		impexFileChooser.setAcceptAllFileFilterUsed(false);
    	}
    	
    	return impexFileChooser;
    }
    
    private int showFileChooserOpenDialog(){
    	return getFileChooser().showOpenDialog(this);
    }
    
    private int showFileChooserSaveDialog(){
    	return getFileChooser().showSaveDialog(this);
    }

	public JButton getJExportButton() {
		if(jExportButton == null){
			jExportButton	= new JButton("export");
			jExportButton.addActionListener(new ActionListener(){
				public void actionPerformed(ActionEvent e) {
					int result = showFileChooserSaveDialog();
					if(result == JFileChooser.APPROVE_OPTION){
						File file = getFileChooser().getSelectedFile();
						String extension = getExtension(file.getName());
						TableExport exporter = tableExporters.get(extension);
						if(exporter != null){
							OutputStream stream;
							try {
								stream = new FileOutputStream(file);
								try {
									if(exporter.exportData(stream, tableModel.getTaskList(), new String[]{"name", "done", "dueDate"})){
										Logger.addMessage("INFO: Data exported to "+file.getName()+" ("+tableModel.getTaskList().size()+" items)");
									}
								} catch (LoaderException e1) {
									e1.printStackTrace();
									Logger.addMessage("ERROR: " + e1.getMessage());
								}
								stream.close();
							} catch (FileNotFoundException e1) {
								e1.printStackTrace();
								Logger.addMessage("ERROR: " + e1.getMessage());
							} catch (IOException e1) {
								e1.printStackTrace();
								Logger.addMessage("ERROR: " + e1.getMessage());
							}
							
							
						}
					}
				}
			});
		}
		return jExportButton;
	}
	
	

	public JButton getJImportButton() {
		if(jImportButton == null){
			jImportButton	= new JButton("import");
			jImportButton.addActionListener(new ActionListener(){
				public void actionPerformed(ActionEvent e) {
					int result = showFileChooserOpenDialog();
					
					if(result == JFileChooser.APPROVE_OPTION){
						File file = getFileChooser().getSelectedFile();
						String extension = getExtension(file.getName());
						TableExport exporter = tableExporters.get(extension);
						if(exporter != null){
							InputStream stream;
							try {
								stream = new FileInputStream(file);
								List<Task> tasks	= null;
								try {
									tasks = exporter.importData(Task.class, stream);
								} catch (LoaderException e1) {
									e1.printStackTrace();
									Logger.addMessage("ERROR: " + e1.getMessage());
								}
								if(tasks != null){
									tableModel.getTaskList().addAll(tasks);
									tableModel.fireTableDataChanged();
									Logger.addMessage("INFO: Data imported from "+file.getName()+" ("+tasks.size()+" items)");
								}
								stream.close();
							} catch (Exception e1) {
								e1.printStackTrace();
								Logger.addMessage("ERROR: " + e1.getMessage());
							}
							
							
						}
					}
				}
			});
			
		}
		return jImportButton;
	}

	public JComboBox getErrorBox() {
		if(errorBox == null){
			errorBox	= new JErrorComboBox();
			Logger.registerLogger(errorBox);
		}
		return errorBox;
	}

	public JButton getJArchiveButton() {
		if(jArchiveButton == null){
			jArchiveButton = new JButton("archive");
			jArchiveButton.addActionListener(new ActionListener(){
				public void actionPerformed(ActionEvent e) {
					new ArchiverUtil().write(tableModel.getTaskList());
					tableModel.fireTableDataChanged();
				}
			});
			
			JPopupMenu popup	= new JPopupMenu();
			
			String[] files = new ArchiverUtil().listArchiveFiles();
			if(files != null)
				for(String file: files){
					popup.add(file);
				}
			jArchiveButton.setComponentPopupMenu(popup);
			
		}
		
		return jArchiveButton;
	}


}  //  @jve:decl-index=0:visual-constraint="10,10"
